package com.hxk.juc;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 读写锁
 *      读写又可拆分为：
 *          共享锁：读锁
 *          独占锁：写锁
 * `juc.ReentrantLock`和`synchronized`都是【独占锁】， 独占锁就是【一个锁】只能被【一个线程】所持有
 *  但是在我们实际的业务场景中，有的时候，需要【读写分离】，那么就要引入读写锁
 *  在这种情况下，那么就要引入uc.ReentrantReadWriteLock`读写锁
 */
public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        //5个线程写入数据
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
           new Thread(()->{
               myCache.put("K_"+temp,"V_"+temp);
               },"W"+1).start();
        }

        // 5个线程读取数据
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.get("K_"+temp);
                }, "R"+i
            ).start();
        }
    }
}

class MyCache{

    //缓冲的更新很快，必须使用volatile通知
    private volatile Map<String, Object> map = new HashMap<>();

    private ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();


    public void put(String key, Object val){
        rrwl.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t 正在写入 key："+ key);
           //模拟网络延迟,这里必须注意，因为咱们在外层已经加了Exception异常的try块，程序提示是可以不用给sleep方法加try的，但是不加程序会进入阻塞状态
            try {TimeUnit.MILLISECONDS.sleep(300);} catch (Exception e) {e.printStackTrace();}
            map.put(key,val);
            System.out.println(Thread.currentThread().getName()+"\t 写入完成 val："+val);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            rrwl.writeLock().unlock();
        }
    }

    public void get(String key){
        rrwl.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t 正在读取 key："+key);
            //模拟网络延迟,这里必须注意，因为咱们在外层已经加了Exception异常的try块，程序提示是可以不用给sleep方法加try的，但是不加程序会进入阻塞状态，其实底层是通过向上抛InterruptedException异常来打断sleep线程
            try {TimeUnit.MILLISECONDS.sleep(300);} catch (Exception e) {e.printStackTrace();}
            Object val = map.get(key);
            System.out.println(Thread.currentThread().getName()+"\t 读取完成 val："+val);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rrwl.readLock().unlock();
        }
    }


}
